from django.shortcuts import render
from rest_framework.views import APIView
from .serializers import CartSerializer, SKUCartSerializer, CartDeletedSerializer, CartSelectedAllSerializer
import pickle, base64
from rest_framework.response import Response
from rest_framework import status
from django_redis import get_redis_connection
from goods.models import SKU

# Create your views here.

class CartView(APIView):
    """购物车增删改查"""

    # 重写 APIView 中的perform_authentication方法 直接pass 可以延后认证 延后到第一次通过请求对象request.user或者request.auth 才去做认证
    def perform_authentication(self, request):
        """
                Perform authentication on the incoming request.

                Note that if you override this and simply 'pass', then authentication
                will instead be performed lazily, the first time either
                `request.user` or `request.auth` is accessed.
        """
        pass

    def post(self, request):
        """新增"""
        #创建序列化器 进行反序列化
        serializer = CartSerializer(data=request.data)
        # 调用is_valid 进行校验
        serializer.is_valid(raise_exception=True)
        # 获取校验后的数据
        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')


        try:
            #如果前端请求没有带Authorization 则request.user 报错 user则会匿名用户
            user = request.user #执行此行代码时 会执行认证逻辑 如果是登录用户认证会成功没异常 但是未登录用户认证会出异常我们自己拦截
        except:
            user = None
        # is_authenticated() 这个方法判断是匿名用户还是登录用户（判断用户是否通过认证）
        # 创建响应对象
        response = Response(serializer.data, status=status.HTTP_201_CREATED)
        if user and user.is_authenticated:
            #"""""" 表示没有接受的字符串 #表示的是单行注释
            """登录用户操作redis购物车数据"""
            """
                hash: {'sku_id_1':2,'sku_id_16':1}
                set: {sku_id_1}
            """
            # 创建redis连接对象
            redis_conn = get_redis_connection('cart')
            pl = redis_conn.pipeline() # 创建管道
            # 添加 如果添加到sku_id 在hash 中已经存在 需要做增量
            # redis_conn.hincrby('cart_%d' % user.id, sku_id, count)
            # redis_conn.hincrby('cart_%d' % user.id, sku_id_1, 1)
            # 如果要添加的sku_id 在hash字典中不存在 就是新增， 如果存在 就自动做增量处理
            pl.hincrby('cart_%d' % user.id, sku_id, count)
            #把勾选的商品sku_id 存储到set集合中
            if selected: #判断当前商品是否勾选 勾选再向set集合中添加
                pl.sadd('selected_%d' % user.id, sku_id)

            pl.execute()

            # return Response(serializer.data, status=status.HTTP_201_CREATED)

        else:
            # """未登录用户操作cookie购物车数据"""
            # """
            # {
            #     'sku_id_1': {'count':1, 'selected':True},
            #     'sku_id_16': {'count':1, 'selected':True},
            # }
            # """
            # 获取cookie购物车数据
            cart_str = request.COOKIES.get('cart')
            if cart_str:
                #说明cookir购物车已经有商品
                #把字符串转成bytes类型的字符串
                cart_str_bytes = cart_str.encode()
                #把bytes类型的字符串转换成bytes类型
                cart_bytes = base64.b64decode(cart_str_bytes)
                # 把bytes类型转换成字典
                cart_dict = pickle.loads(cart_bytes)
            else:
                # 如果cookie购物车还没有数据 说明是第一次添加
                cart_dict = {}

            # 增量计算
            if sku_id in cart_dict:
                # 判断当前要添加的sku_id 在字典中是否已存在
                origin_count = cart_dict[sku_id]['count']
                count += origin_count
            # 把新的商品添加到cart_dict字典中
            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }

            # 先将字典转换成bytes类型
            cart_bytes = pickle.dumps(cart_dict)
            # 再将bytes类型转换成bytes类型的字符串
            cart_str_bytes = base64.b64encode(cart_bytes)
            #把bytes类型的字符串 转换成字符串
            cart_str = cart_str_bytes.decode()

            # 创建响应对象
            # response = Response(serializer.data, status=status.HTTP_201_CREATED)
            # 设置cookie 设置参数：expires 为 过期时间
            response.set_cookie('cart', cart_str)

        return response


    def get(self, request):
        """查询"""
        try:
            user = request.user
        except:
            user = None
        if user and user.is_authenticated:
            """登录用户获取redis购物车数据"""
            #创建redis连接对象
            redis_conn = get_redis_connection('cart')
            #获取hash数据{sku_id_1:1}
            cart_redis_dict = redis_conn.hgetall('cart_%d' % user.id)
            #获取set集合数据
            selecteds = redis_conn.smembers('selected_%d' % user.id)
            #将redis购物车数据格式转换成和cookie购物车数据格式一致
            cart_dict = {}
            for sku_id_bytes, count_bytes in cart_redis_dict.items():#遍历hash字典
                cart_dict[int(sku_id_bytes)] = {
                    'count': int(count_bytes),
                    'selected': sku_id_bytes in selecteds #判断在selecteds 是否存在 返回True或者False
                }

        else:
            """未登录用户获取cookie购物车数据"""
            cart_str = request.COOKIES.get('cart')
            if cart_str:
                cart_str_bytes = cart_str.encode()
                cart_bytes = base64.b64decode(cart_str_bytes)
                cart_dict = pickle.loads(cart_bytes)
            else:
                return Response({'message': '没有购物车数据'}, status=status.HTTP_400_BAD_REQUEST)

        # 根据sku_id 查询sku模型
        sku_ids = cart_dict.keys()
        #直接查询出 所有的sku模型 返回查询集
        skus = SKU.objects.filter(id__in=sku_ids)
        # 给每个sku模型多定义一个count和selected属性
        for sku in skus:
            #首次赋值一个属性 会临时的增加这个属性
            sku.count = cart_dict[sku.id]['count']
            sku.selected = cart_dict[sku.id]['selected']
        # 创建序列化器 进行序列化
        serializer = SKUCartSerializer(skus, many=True)

        return Response(serializer.data)


    def put(self, request):
        """修改"""

        # 创建序列化器
        serializer = CartSerializer(data=request.data)
        # 校验
        serializer.is_valid(raise_exception=True)
        # 获取校验后的数据
        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')

        try:
            user = request.user
        except:
            user = None
        # 创建响应对象
        response = Response(serializer.data)
        if user and user.is_authenticated:
            """登录用户修改redis购物车数据"""
            # 创建redis连接对象
            redis_conn = get_redis_connection('cart')
            # 因为hset/sadd/srem 都是redis设置指令 没有获取redis的指令  创建管道对象 提升redis性能
            pl = redis_conn.pipeline()
            # 覆盖sku_id 对应的count
            pl.hset('cart_%d' % user.id, sku_id, count)
            # 如果勾选把勾选商品的sku_id 存储到set集合中
            if selected:
                pl.sadd('selected_%d' % user.id, sku_id)
            else:
                # 如果未勾选 把商品的sku_id 从set集合移除
                pl.srem('selected_%d' % user.id, sku_id)
            # 执行管道
            pl.execute()

            return  Response(serializer.data)

        else:
            """未登录用户修改cookie购物车数据"""
            cart_str = request.COOKIES.get('cart')
            # 判断cookie有没有取到
            if cart_str:
                # 把cookie字符串转成字典
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                # 没有cookie取出 提前响应 不执行后续代码
                return Response({'message': '没有获取到cookie'}, status=status.HTTP_400_BAD_REQUEST)
            # 直接覆盖原cookie数据
            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }
            # 把cookie大字典 再转成字符串
            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            # 创建响应对象
            # response = Response(serializer.data)
            # 设置cookie
            response.set_cookie('cart', cart_str)

        return response


    def delete(self, request):
        """删除"""
        # 创建序列化器
        serializer = CartDeletedSerializer(data=request.data)
        #校验
        serializer.is_valid(raise_exception=True)
        # 获取校验后的数据 sku_id
        sku_id = serializer.validated_data.get('sku_id')
        try:
            user = request.user
        except:
            user = None

        # 创建响应对象
        response = Response(status=status.HTTP_204_NO_CONTENT)
        if user and user.is_authenticated:
            """登录用户操作redis购物车数据"""
            # 创建redis连接对象
            redis_conn = get_redis_connection('cart')

            pl = redis_conn.pipeline()
            # 删除hash数据
            pl.hdel('cart_%d' % user.id, sku_id)
            # 删除set数据
            pl.srem('selected_%d' % user.id, sku_id)

            pl.execute()

        else:
            """未登录用户操作cookie购物车数据"""
            # 获取cookie
            cart_str = request.COOKIES.get('cart')
            # 判断是否获取cookie
            if cart_str:
                #把cookie字符串数据转换成cookie字典
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            # 如果没有过去到cookie 提前响应
            else:
                return Response({'message': 'cookie没有获取到'}, status=status.HTTP_400_BAD_REQUEST)

            # 要删除的sku_id 从cookie大字典中移除键值对
            if sku_id in cart_dict: # 判断要删除的sku_id 是否存在字典中 存在 再去删除
                del cart_dict[sku_id]
            if len(cart_dict.keys()):# 如果cookie字典中还存在商品

                # 再把cookie字典转换成cookie 字符串
                cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
                # 设置cookie
                response.set_cookie('cart', cart_str)
            else:
                # cookie购物车数据已经清空 直接将购物车的cookie删除
                response.delete_cookie('cart')

        return response




class CartSelectedAllView(APIView):
    """购物车全选"""

    # 重写 APIView 中的perform_authentication方法 直接pass 可以延后认证 延后到第一次通过请求对象request.user或者request.auth 才去做认证
    def perform_authentication(self, request):
        """
                Perform authentication on the incoming request.

                Note that if you override this and simply 'pass', then authentication
                will instead be performed lazily, the first time either
                `request.user` or `request.auth` is accessed.
        """
        pass

    def put(self, request):
        """全选"""
        serializer = CartSelectedAllSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        selected = serializer.validated_data.get('selected')

        try:
            user = request.user
        except:
            user = None

        response = Response(serializer.data)
        if user and user.is_authenticated:
            """登录用户操作redis购物车数据"""
            # 创建redis连接对象
            redis_conn = get_redis_connection('cart')
            # 获取hash字典中的所有数据
            cart_redis_dict = redis_conn.hgetall('cart_%d' % user.id)
            # hash字典中所有key
            sku_ids = cart_redis_dict.keys()
            # 判断前端selected是True还是False
            if selected:
                # 如果是True 把所有的sku_id 添加到set集合中
                redis_conn.sadd('selected_%d' % user.id, *sku_ids)
            else:
                # 如果是False 把所有的sku_id 从set集合中删除
                redis_conn.srem('selected_%d' % user.id, *sku_ids)

        else:
            """未登录用户操作cookie购物车数据"""
            # 先获取cookie数据
            cart_str = request.COOKIES.get('cart')
            # 判断cookie购物车数据是否有值
            if cart_str:
                # 把字符串转换成字典
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            # 如果cookie购物车数据没有取出
            else:
                #提前响应
                return Response({'message': 'cookie没有获取到'}, status=status.HTTP_400_BAD_REQUEST)
            # 遍历cookie大字典 根据前端传入的selected来修改商品的选中状态
            for sku_id in cart_dict:
                cart_dict[sku_id]['selected'] = selected
            # 再将字典转换成字符串
            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            # 设置cookie
            response.set_cookie('cart', cart_str)

        return response



